package com.cskaoyan.javase.object._5clone;

/**
 * @description: clone方法
 * @author: wuguidong@cskaoyan.onaliyun.com
 **/

/**
 * 生物学上的clone，是由物种A的体细胞经过无性繁殖，生成基因型完全一致的后代的过程
 * Java问世时，生物学上clone技术已经风靡全球，所以Java中的clone方法和生物学中的克隆有类似性
 * 所以Java中的克隆就是复制一个和之前对象一模一样但又完全独立的一个新对象
 *
 * clone方法的作用(Object类当中的默认实现):
 *      1.生成一个完全独立的对象
 *      2.一模一样的对象,指的是成员变量的取值是一样的
 *
 * clone方法的声明:
 *     protected native Object clone() throws CloneNotSupportedException;
 *       a.首先该方法的访问权限是protected,所以我们在使用clone方法时首先要解决的就是它的权限问题
 *       b.需要注意该方法是本地方法,它的作用复制一个和之前对象一模一样但又完全独立的一个新对象
 *       c.返回值类型是Object,但是子类可以自己重写返回值类型
 *       d.该方法不需要参数
 *       e.throws CloneNotSupportedException 这仍然是方法抛出异常的声明
 *
 * CloneNotSupportedException: 不支持克隆的异常,
 *  在Java当中想要完成对某个对象的克隆,那么它的类型必须实现一个接口----Cloneable(能够克隆),表示该类型可以支持克隆
 *  当一个类型没有实现该接口,它的对象却仍想克隆时,就会报错抛出异常CloneNotSupportedException
 *
 * 使用clone方法的步骤:
 *      1.重写clone方法的访问权限,否则只能自己克隆自己没有意义
 *      2.(选做)重写它的返回值类型,明确返回对象的具体类型
 *      3.让该类型实现接口Cloneable
 *      4.以上,完成对某个对象的克隆
 *
 * 注意事项:
 *      1.Cloneable接口,实际上是一个空接口,里面没有任何抽象方法
 *          那么空接口Cloneable有何意义?
 *          一个类实现空接口有什么意义?
 *          一个类实现空接口,虽然没有获取任何成员,但是改变了它的数据类型,从不是该接口的子类,变成了它的子类
 *          变成一个接口的子类后,就可以使用instanceof判断,从而完成一系列的操作
 *          空接口是否实现改变了类型的定义,让某个类型成为了接口的子类,从而能够使用instanceof去判断
 *              然后完成一系列操作
 *          clone当中Cloneable接口的实现原理就是这样的
 *          空接口根据它的这一特点,也被称之为"标记接口"
 *      2.在多数情况下,我们使用clone方法都直接使用Object类当中的本地方法默认实现,一般不会自己重写该方法
 *          如果想重写,它的原则:
 *          ​	对于任何对象 x，表达式：
 * ​		        1，x.clone() != x 为 true（这一条是必须的,克隆后的对象应该是独立的）
 * ​		        2，x.clone().getClass() == x.getClass() 为true (类型不变,这一条在多数情况下应该满足)
 * ​		        3，x.clone().equals(x) 为true
 *    （一般情况下为true，但这并不是必须要满足的要求,如果按照成员变量取值重写了equals方法这一条是需要满足,否则不需要）
 *      3.clone方法创建对象不依赖于构造器,它是一种全新的和new对象平行的创建对象的方式
 *              创建对象的四种方式:
 *                  1.new
 *                  2.克隆
 *                  3.反射
 *                  4.序列化反序列化(标记接口)
 *      4.如果想要克隆一个具有引用数据类型成员变量的对象,会有什么不同呢?
 *           对象当中具有引用数据类型的成员变量,
 *           那么克隆后的对象中拷贝引用仍然指向原先的对象,这样就不符合克隆后的对象完全独立的原则了
 *      浅拷贝和深拷贝：
 *          浅拷贝：
 *               被复制对象的，所有基本类型成员变量值，都与原来对象的相同，且独立
 *               被复制对象的，所有引用类型的引用，仍然指向原来的对象，相当于复制了对象引用，而没有复制对象
 *               Object类当中的默认clone方法实现就是浅拷贝
 *          深克隆(深拷贝):
 *               在浅拷贝的基础上，复制对象引用的同时，也复制了对象，并让复制的引用指向了复制的对象
 *
 */
public class Demo {
    public static void main(String[] args) throws CloneNotSupportedException {
        /*//不重写Student类的clone方法,在Demo类当中是不能调用它的方法
        Student s = new Student(18, 0);
        Student stuClone = s.clone();
        System.out.println(s == stuClone); //false
        System.out.println(s);
        System.out.println(stuClone);*/

        Student stu = new Student(18, 100, new Star(1000));
        Student cloneStu = stu.clone();
        cloneStu.s.salary = 2000;
        System.out.println(stu.s.salary);


    }
}

class Student implements Cloneable {
    int age;
    double score;

    Star s;

    public Student() {
    }

    public Student(int age, double score) {
        //System.out.println("hello world!");
        this.age = age;
        this.score = score;
    }

    public Student(int age, double score, Star s) {
        this.age = age;
        this.score = score;
        this.s = s;
    }

    //要想使用Student类的clone方法,必须在Student类当中,才能使用,但是自己克隆自己没有意义,所以需要重写访问权限
    @Override
    protected Student clone() throws CloneNotSupportedException {
        //1.深克隆是在浅克隆基础上完成的
        Student cloneStu = (Student) super.clone();
        //2.克隆Star对象,需要注意权限和空接口的问题
        Star cloneStar = cloneStu.s.clone();
        //3.将拷贝后的引用s指向新对象
        cloneStu.s = cloneStar;
        //4.完成深度克隆,返回克隆完毕Student对象
        return cloneStu;
    }

    @Override
    public String toString() {
        return "Student{" +
                "age=" + age +
                ", score=" + score +
                '}';
    }
}
//interface CloneableSon extends Cloneable{}

class Star implements Cloneable{
    double salary;

    public Star() {
    }

    public Star(double salary) {
        this.salary = salary;
    }

    @Override
    protected Star clone() throws CloneNotSupportedException {
        return ((Star) super.clone());
    }

}